package self.zhangkang.chart.service.main

import self.zhangkang.chart.data.BOLL
import self.zhangkang.chart.data.KLineInfo
import kotlin.math.sqrt

/**
 * @author : Android-张康
 * created on: 2023/9/7 17:13
 * description: 布林值计算工具
 */
class BOLLTools<T>(private val N: Int, private val getClosePrice: (T) -> Float) {

    companion object {

        private const val BOLL_20 = 20

        fun calculationBOLL(dataList: MutableList<KLineInfo>) {
            val bollTools = BOLLTools<KLineInfo>(BOLL_20) {
                it.closePrice
            }
            dataList.forEachIndexed { index, stockInfo ->
                val boll = bollTools.calculationBOLL(dataList, index)
                dataList[index] = stockInfo.copy(boll = boll)
            }
        }

    }

    fun calculationBOLL(
        dataList: List<T>,
        currentIndex: Int,
    ): BOLL {
        if (currentIndex < N - 1) {
            return BOLL()
        }
        val mb: Float = calculateMovingAverage(dataList, currentIndex, getClosePrice)
        val sumMD = calculateSum(dataList, currentIndex) {
            val closePrice = getClosePrice(it)
            // C-MB
            val cma = closePrice - mb
            return@calculateSum cma * cma
        }
        //MD=前n日C-MB的平方和来开根
        val md = sqrt((sumMD / (N - 1)).toDouble()).toFloat()
        return BOLL(
            // UP=MB+2*MD
            up = mb + 2 * md,
            mid = mb,
            // DN=MB-2*MD
            down = mb - 2 * md
        )
    }

    /**
     * 计算移动平均值
     *
     * @param dataList K线数据列表
     * @param currentIndex 当前的位置
     *
     * @return 返回 [currentIndex] 位置的 [N] 日移动平均值
     */
    fun calculateMovingAverage(
        dataList: List<T>,
        currentIndex: Int,
        getValue: (T) -> Float
    ): Float {
        if (currentIndex + 1 < N) {
            return Float.NaN
        }
        return calculateSum(dataList, currentIndex, getValue).div(N)
    }

    /**
     * 计算移动平均值
     *
     * @param dataList K线数据列表
     * @param currentIndex 当前的位置
     *
     * @return 返回 [currentIndex] 位置的 [N] 日移动平均值
     */
    fun calculateSum(
        dataList: List<T>,
        currentIndex: Int,
        getValue: (T) -> Float
    ): Float {
        if (currentIndex + 1 < N) {
            return Float.NaN
        }
        var value: Float = getValue(dataList[currentIndex])
        for (index in currentIndex - N + 1 until currentIndex) {
            value += getValue(dataList[index])
        }
        return value
    }

}